#include <string.h>
#include "door_common.h"
#include "gkt_io_debounce.h"
#include "gkt_door.h"
#if defined(GKT_CONFIG_FACTORY_RESET_ENABLE) && GKT_CONFIG_FACTORY_RESET_ENABLE
#include "gkt_factory_reset.h"
#endif

#if defined(GKT_CONFIG_DOOR_LOCK_ENABLE) && GKT_CONFIG_DOOR_LOCK_ENABLE \
	&& defined(GKT_CONFIG_DOOR_LOCK_AUTOMATIC) && GKT_CONFIG_DOOR_LOCK_AUTOMATIC \
	&& (GKT_MODULE_LOC_FRONTBOARD == GKT_DOOR_LOCK_LOCATION) \
	&& defined(GKT_CONFIG_DOOR_KEY_ENABLE) && GKT_CONFIG_DOOR_KEY_ENABLE

#ifndef GKT_CONFIG_DOOR_KEY_DETECT_LEVEL
#error "not define GKT_CONFIG_DOOR_KEY_DETECT_LEVEL"
#endif

#define DOOR_KEY_DETECT_LEVEL	GKT_CONFIG_DOOR_KEY_DETECT_LEVEL
#if (0 == DOOR_KEY_DETECT_LEVEL)
#define DOOR_KEY_WKIO_POLARITY	GKT_WKIO_POL_FALL_EDGE
#define DOOR_KEY_GPIO_FLAGS	\
	(GKT_GPIO_F_INPUT | GKT_GPIO_F_INPUT_PULL_UP | GKT_GPIO_F_INT_EDGE_FALL \
		| GKT_GPIO_F_INT_DEBOUNCE | GKT_GPIO_F_INT_MASKED)
#else
#define DOOR_KEY_WKIO_POLARITY	GKT_WKIO_POL_RAISE_EDGE
#define DOOR_KEY_GPIO_FLAGS	\
	(GKT_GPIO_F_INPUT | GKT_GPIO_F_INPUT_PULL_DOWN | GKT_GPIO_F_INT_EDGE_RAISE \
		| GKT_GPIO_F_INT_DEBOUNCE | GKT_GPIO_F_INT_MASKED)
#endif

#if defined(GKT_CONFIG_DOOR_KEY_IO_DEBOUNCE_TIME_MS) \
	&& (GKT_CONFIG_DOOR_KEY_IO_DEBOUNCE_TIME_MS >= 20)
#define DOOR_KEY_IO_DEBOUNCE_TIME_MS	\
	GKT_CONFIG_DOOR_KEY_IO_DEBOUNCE_TIME_MS
#else
#define DOOR_KEY_IO_DEBOUNCE_TIME_MS	50
#endif

#if GKT_DOOR_KEY_COMBINED_FACTORY_RESET_ENABLE
#define DOOR_KEY_COMBINED_FACTORY_RESET_KEY_BITS	\
	((1U << GKT_DOOR_KEY_OPEN) | (1U << GKT_DOOR_KEY_DOUBLE_LOCK))
#endif

#define DOOR_KEY_ST_IO_READY	(1U << 0)
#define DOOR_KEY_ST_IO_DEBOUNCE_READY	(1U << 1)
#define DOOR_KEY_ST_IO_DEBOUNCE_BUSY	(1U << 2)

#define DOOR_KEY_ST_WAKEUP	(1U << 4)
#define DOOR_KEY_ST_PRESSED	(1U << 5)
#define DOOR_KEY_ST_RELEASE	(1U << 6)

#define DOOR_KEY_SEQ_INVALID	0
#define DOOR_KEY_SEQ_INITIAL	(DOOR_KEY_SEQ_INVALID + 1)

typedef struct _door_key_item {
	uint32_t	gpio_pin;
	uint32_t	io_debounce_id;
	uint32_t	state;
	uint32_t	seq;
} door_key_item_s;

#define DOOR_SINGLE_KEY_MAX	(GKT_DOOR_KEY_DOUBLE_LOCK + 1)
typedef struct _door_key_fb {
	int	init_ok;

	gkt_door_key_e	wakeup_key;
	uint32_t	mask_bits;
	uint32_t	seq;
	uint32_t	combined_factory_reset_notify_seq;
	door_key_item_s	key_items[DOOR_SINGLE_KEY_MAX];
} door_key_fb_s;
static door_key_fb_s s_door_key_fb;

static int door_key_gpio_is_detected(uint32_t gpio_pin)
{
	uint32_t level;
	int retval;

	retval = gkt_gpio_get_pin_level(gpio_pin, &level);
	return ((GKT_SUCCESS == retval) && (DOOR_KEY_DETECT_LEVEL == level)) ? 1 : 0;
}

static void door_key_start_io_debounce(door_key_item_s *key_item,
				uint32_t duration_ms, int detect_state)
{
	int retval;

	gkt_info("door_key_iodb_start: (%u)(0x%x) seq(0x%x)\n",
		key_item - &s_door_key_fb.key_items[0], 
		key_item->state, key_item->seq);

	key_item->state |= DOOR_KEY_ST_IO_DEBOUNCE_BUSY;
	retval = gkt_io_debounce_start(key_item->io_debounce_id, 
				detect_state, duration_ms);
	if (retval != GKT_SUCCESS) {
		key_item->state &= ~DOOR_KEY_ST_IO_DEBOUNCE_BUSY;
		gkt_error("door_key_iodb_start: (%u) failed.\n",
				key_item - &s_door_key_fb.key_items[0]);
	}
}

#if GKT_DOOR_KEY_COMBINED_FACTORY_RESET_ENABLE
static void door_key_check_factory_reset(void)
{
	uint32_t pressed_bits, i;
	int wakeup;

	if (!(s_door_key_fb.mask_bits & (1U << GKT_DOOR_KEY_COMBINED_FACTORY_RESET))) {
		wakeup = 0;
		pressed_bits = 0;
		for (i = 0; i < DOOR_SINGLE_KEY_MAX; i++) {
			if (((1U << i) & DOOR_KEY_COMBINED_FACTORY_RESET_KEY_BITS)
				&& (s_door_key_fb.key_items[i].state & DOOR_KEY_ST_PRESSED)
				&& (s_door_key_fb.key_items[i].seq == s_door_key_fb.seq)) 
			{
				if (s_door_key_fb.key_items[i].state & DOOR_KEY_ST_WAKEUP)
					wakeup = 1;
				pressed_bits |= 1U << i;
			}
		}

		gkt_info("door_key_iodb_fr: pressed_bits(0x%x) mask(0x%x) seq(0x%x/0x%x)\n",
			pressed_bits, s_door_key_fb.mask_bits, s_door_key_fb.seq, 
			s_door_key_fb.combined_factory_reset_notify_seq);
		if ((pressed_bits == DOOR_KEY_COMBINED_FACTORY_RESET_KEY_BITS)
			&& (s_door_key_fb.combined_factory_reset_notify_seq != s_door_key_fb.seq)) 
		{
			s_door_key_fb.combined_factory_reset_notify_seq = s_door_key_fb.seq;
			if (wakeup)
				s_door_key_fb.wakeup_key = GKT_DOOR_KEY_COMBINED_FACTORY_RESET;
			door_inner_notify(GKT_DOOR_NOTIFY_KEY, GKT_DOOR_KEY_COMBINED_FACTORY_RESET);
		}
	}
}
#else
#define door_key_check_factory_reset()	do {} while (0)
#endif

static void door_key_io_debounce_complete(gkt_door_key_e key,
				int result, int detect_state)
{
	door_key_item_s *key_item;
	gkt_door_key_e i;
	uint32_t key_nums, pressed_bits, release_bits;

	key_item = &s_door_key_fb.key_items[key];
	gkt_info("door_key_iodb_end: key(%u)(0x%x) (%d - %d)\n", 
		key, key_item->state, result, detect_state);

	if (key_item->state & DOOR_KEY_ST_IO_DEBOUNCE_BUSY) {
		if (GKT_IO_DEBOUNCE_OK == result) {
			if (detect_state) {
				key_item->state |= DOOR_KEY_ST_PRESSED;
				door_key_start_io_debounce(key_item, GKT_INFINITE, 0);
				door_key_check_factory_reset();
			}
			else if (key_item->state & DOOR_KEY_ST_PRESSED) {
				key_item->state &= ~DOOR_KEY_ST_IO_DEBOUNCE_BUSY;
				key_item->state |= DOOR_KEY_ST_RELEASE;

				key_nums = 0;
				pressed_bits = 0;
				release_bits = 0;
				gkt_irq_global_disable();
				for (i = 0; i < DOOR_SINGLE_KEY_MAX; i++) {
					if (s_door_key_fb.key_items[i].seq == s_door_key_fb.seq) {
						if (s_door_key_fb.key_items[i].state & DOOR_KEY_ST_PRESSED) {
							key_nums++;
							pressed_bits |= 1U << i;
						}
						if (s_door_key_fb.key_items[i].state & DOOR_KEY_ST_RELEASE)
							release_bits |= 1U << i;
					}
				}
				if (release_bits && (release_bits == pressed_bits)) {
					s_door_key_fb.seq++;
					if (DOOR_KEY_SEQ_INVALID == s_door_key_fb.seq)
						s_door_key_fb.seq = DOOR_KEY_SEQ_INITIAL;
				}
				gkt_irq_global_enable();

				gkt_info("door_key_iodb_end: bits(0x%x - 0x%x) mask(0x%x)\n", 
					pressed_bits, release_bits, s_door_key_fb.mask_bits);
				if (!(s_door_key_fb.mask_bits & (1U << key)) && (1 == key_nums)) {
					if (key_item->state & DOOR_KEY_ST_WAKEUP)
						s_door_key_fb.wakeup_key = key;
					door_inner_notify(GKT_DOOR_NOTIFY_KEY, key);
				}
			}
			else {
				/* release without pressed? */
				key_item->state &= ~DOOR_KEY_ST_IO_DEBOUNCE_BUSY;
			}
		} 
		else
			key_item->state &= ~DOOR_KEY_ST_IO_DEBOUNCE_BUSY;
	}
}
static void door_key_detect_notify(uint32_t pin)
{
	door_key_item_s *key_item;
	gkt_door_key_e key;

	for (key = 0; key < DOOR_SINGLE_KEY_MAX; key++) {
		key_item = &s_door_key_fb.key_items[key];
		if ((key_item->gpio_pin == pin)
			&& (key_item->state & DOOR_KEY_ST_IO_READY)) 
		{
			gkt_info("door_key_notify: (%u)(0x%x)\n", key, key_item->state);

			if ((key_item->state & DOOR_KEY_ST_IO_DEBOUNCE_READY)
				&& !(key_item->state & DOOR_KEY_ST_IO_DEBOUNCE_BUSY)) 
			{
				key_item->state &= ~(DOOR_KEY_ST_WAKEUP | DOOR_KEY_ST_PRESSED | DOOR_KEY_ST_RELEASE);
				key_item->seq = s_door_key_fb.seq;
				door_key_start_io_debounce(key_item, DOOR_KEY_IO_DEBOUNCE_TIME_MS, 1);
			}

			break;
		}
	}
}

static int door_key_gpio_init(door_key_item_s *key_item)
{
	int retval;

	retval = gkt_gpio_config(key_item->gpio_pin,
					DOOR_KEY_GPIO_FLAGS, door_key_detect_notify);
	if (retval != GKT_SUCCESS) {
		gkt_error("door_key_init: <1> config gpio failed %d\n", retval);
		return retval;
	}
	key_item->state |= DOOR_KEY_ST_IO_READY;
	if (door_key_gpio_is_detected(key_item->gpio_pin)) {
		gkt_wakeup_set_source(GKT_WAKEUP_SRC_door_key);

		key_item->state |= DOOR_KEY_ST_WAKEUP;
		key_item->seq = s_door_key_fb.seq;
		door_key_start_io_debounce(key_item, DOOR_KEY_IO_DEBOUNCE_TIME_MS, 1);
	}

	gkt_gpio_interrupt_unmask(key_item->gpio_pin);

	return GKT_SUCCESS;
}

#ifdef GKT_CONFIG_DOOR_KEY_OPEN_GPIO_PIN
static int door_key_open_is_detected(void)
{
	return door_key_gpio_is_detected(GKT_CONFIG_DOOR_KEY_OPEN_GPIO_PIN);
}
static void door_key_open_debounce_complete(
				int result, int detect_state)
{
	gkt_unused(detect_state);
	door_key_io_debounce_complete(GKT_DOOR_KEY_OPEN, result, detect_state);
}
static int door_key_open_init(void)
{
	door_key_item_s *key_item;
	gkt_io_debounce_attr_s io_debounce_attr;
	int retval;

	key_item = &s_door_key_fb.key_items[GKT_DOOR_KEY_OPEN];
	key_item->gpio_pin = GKT_CONFIG_DOOR_KEY_OPEN_GPIO_PIN;
	key_item->io_debounce_id = GKT_IO_DEBOUNCE_ID(door_key_open);

	io_debounce_attr.detect_fn = door_key_open_is_detected;
	io_debounce_attr.complete_fn = door_key_open_debounce_complete;
	retval = gkt_io_debounce_config(key_item->io_debounce_id, &io_debounce_attr);
	if (GKT_SUCCESS == retval)
		key_item->state |= DOOR_KEY_ST_IO_DEBOUNCE_READY;
	else
		gkt_error("door_key_open: debounce config failed!\n");

	return door_key_gpio_init(key_item);
}
#endif

#ifdef GKT_CONFIG_DOOR_KEY_DOUBLE_LOCK_GPIO_PIN
static int door_key_double_lock_is_detected(void)
{
	return door_key_gpio_is_detected(GKT_CONFIG_DOOR_KEY_DOUBLE_LOCK_GPIO_PIN);
}
static void door_key_double_lock_debounce_complete(
				int result, int detect_state)
{
	gkt_unused(detect_state);
	door_key_io_debounce_complete(GKT_DOOR_KEY_DOUBLE_LOCK, result, detect_state);
}
static int door_key_double_lock_init(void)
{
	door_key_item_s *key_item;
	gkt_io_debounce_attr_s io_debounce_attr;
	int retval;

	key_item = &s_door_key_fb.key_items[GKT_DOOR_KEY_DOUBLE_LOCK];
	key_item->gpio_pin = GKT_CONFIG_DOOR_KEY_DOUBLE_LOCK_GPIO_PIN;
	key_item->io_debounce_id = GKT_IO_DEBOUNCE_ID(door_key_double_lock);

	io_debounce_attr.detect_fn = door_key_double_lock_is_detected;
	io_debounce_attr.complete_fn = door_key_double_lock_debounce_complete;
	retval = gkt_io_debounce_config(key_item->io_debounce_id, &io_debounce_attr);
	if (GKT_SUCCESS == retval)
		key_item->state |= DOOR_KEY_ST_IO_DEBOUNCE_READY;
	else
		gkt_error("door_key_double_lock: debounce config failed!\n");

	return door_key_gpio_init(key_item);
}
#endif

int door_key_init(void)
{
	uint32_t key;

	if (s_door_key_fb.init_ok)
		return GKT_SUCCESS;

	s_door_key_fb.wakeup_key = GKT_DOOR_KEY_MAX;
	s_door_key_fb.seq = DOOR_KEY_SEQ_INITIAL;
	s_door_key_fb.combined_factory_reset_notify_seq = DOOR_KEY_SEQ_INVALID;
	for (key = 0; key < DOOR_SINGLE_KEY_MAX; key++)
		s_door_key_fb.key_items[key].seq = DOOR_KEY_SEQ_INVALID;

#ifdef GKT_CONFIG_DOOR_KEY_OPEN_GPIO_PIN
	door_key_open_init();
#endif
#ifdef GKT_CONFIG_DOOR_KEY_DOUBLE_LOCK_GPIO_PIN
	door_key_double_lock_init();
#endif

	s_door_key_fb.init_ok = 1;

	return GKT_SUCCESS;
}

void gkt_door_key_mask(uint32_t mask_bits)
{
	gkt_info("door_key: mask_bits= 0x%x\n", mask_bits);
	s_door_key_fb.mask_bits = mask_bits;
}

gkt_door_key_e gkt_door_key_get_wakeup_key(void)
{
	gkt_printf("door_key: wakeup_key - %u\n", s_door_key_fb.wakeup_key);
	return s_door_key_fb.wakeup_key;
}

int gkt_door_key_is_detected(gkt_door_key_e key)
{
	int detected = 0;

#if GKT_DOOR_KEY_COMBINED_FACTORY_RESET_ENABLE
	if (GKT_DOOR_KEY_COMBINED_FACTORY_RESET == key) {
		gkt_door_key_e i;
		uint32_t key_bits;

		key_bits = 0;
		for (i = 0; i < DOOR_SINGLE_KEY_MAX; i++) {
			if ((1U << i) & DOOR_KEY_COMBINED_FACTORY_RESET_KEY_BITS) {	
				if (!(s_door_key_fb.key_items[i].state & DOOR_KEY_ST_IO_READY)
					|| (s_door_key_fb.key_items[i].seq != s_door_key_fb.seq))
				{
					return 0;
				}

				detected = door_key_gpio_is_detected(s_door_key_fb.key_items[i].gpio_pin);
				if (detected)
					key_bits |= 1U << i;
			}
		}

		return (DOOR_KEY_COMBINED_FACTORY_RESET_KEY_BITS == key_bits) ? 1 : 0;
	}
#endif

	if ((key < DOOR_SINGLE_KEY_MAX)
		&& (s_door_key_fb.key_items[key].state & DOOR_KEY_ST_IO_READY)
		&& (s_door_key_fb.key_items[key].state & DOOR_KEY_ST_PRESSED)
		&& (s_door_key_fb.key_items[key].seq == s_door_key_fb.seq)) 
	{
		detected = door_key_gpio_is_detected(s_door_key_fb.key_items[key].gpio_pin);	
	}

	return detected;
}

#endif

